cont <- params$controlCellLine
exp <- params$experimentalCellLine
countmatrix.all <- params$countmatrix.all
metadata.all <- params$metadata.all
rm(params) # Remove the parameters so that we can make subsequent parameterized calls

Perform analysis of OVCAR3B vs OVCAR3

Gather and organize raw data

metadata.pair <- as.data.frame(metadata.all) %>%
  filter(CellLine == cont | CellLine == exp)
as.data.frame(metadata.pair)
countmatrix.pair <- countmatrix.all[, metadata.pair$ShortName]
as.data.frame(countmatrix.pair)

Differential expression analysis

Run Deseq on the data set.

# Saving time by just loading the dds we already ran (recent changes are all after this point)
load(str_interp("Rdata/${exp}_vs_${cont}_dds.RData"))

Filter DESeq2 results for significant genes

Filter res for padj < 0.05

res.filtered <- as.data.frame(res) %>%
  filter(padj < 0.05)
  # filter(log2FoldChange >= 1.5 | log2FoldChange <= -1.5)
res.filtered <- res.filtered[order(res.filtered$log2FoldChange, decreasing = TRUE),]
res.filtered

Significant “Up” genes (OVCAR3B compared to control OVCAR3)

up.unfiltered <- subset(res, log2FoldChange > 0)
up.unfiltered <- up.unfiltered[order(up.unfiltered$log2FoldChange, decreasing = TRUE), ]
outFile <- str_interp("output/${exp}_vs_${cont}_all_upregulated_genes.csv")
write.csv(up.unfiltered[, c("log2FoldChange", "padj")], file = outFile)
up.unfiltered[, c("log2FoldChange", "padj")]
log2 fold change (MLE): CellLine OVCAR3B vs OVCAR3 
 
DataFrame with 11025 rows and 2 columns
             log2FoldChange        padj
                  <numeric>   <numeric>
RNU1-28P            22.9097 2.97662e-07
SERF1A              22.1122 8.87360e-07
SLX1A               10.4798 7.71015e-02
SLC1A6              10.1774 1.47609e-16
GPIHBP1             10.0672 1.55595e-13
...                     ...         ...
IRF6            0.000489069    0.999507
LOC339192       0.000488599    0.999816
PRDM15          0.000447594    0.999471
LOC101060321    0.000340347    0.999816
ARL6IP4         0.000235277    0.999816
up <- subset(res.filtered, log2FoldChange > 0)
up <- up[order(up$log2FoldChange, decreasing = TRUE), ]
outFile <- str_interp("output/${exp}_vs_${cont}_significantly_upregulated_genes.csv")
write.csv(up[, c("log2FoldChange", "padj")], file = outFile)
print(up[, c("log2FoldChange", "padj")])

Significant “Down” genes (OVCAR3B compared to control OVCAR3)

down.unfiltered <- subset(res, log2FoldChange < 0)
down.unfiltered <- down.unfiltered[order(down.unfiltered$log2FoldChange, decreasing = FALSE), ]
outFile <- str_interp("output/${exp}_vs_${cont}_all_downregulated_genes.csv")
write.csv(down.unfiltered[, c("log2FoldChange", "padj")], file = outFile)
print(down.unfiltered[, c("log2FoldChange", "padj")])
log2 fold change (MLE): CellLine OVCAR3B vs OVCAR3 
 
DataFrame with 10695 rows and 2 columns
              log2FoldChange        padj
                   <numeric>   <numeric>
RNVU1-18           -22.74800 3.76483e-07
LCMT2              -10.48107 8.08058e-15
SMIM10              -8.96520 3.75167e-10
HIST1H4C            -8.88861 1.42353e-03
RP11-168E14.1       -8.67308 2.62124e-17
...                      ...         ...
EFTUD1P1        -2.71424e-04          NA
XPO1            -2.66183e-04    0.999795
NUDT9           -1.69566e-04    0.999816
C5orf51         -5.86787e-05    0.999929
SEC22B          -1.97854e-05    0.999952
down <- subset(res.filtered, log2FoldChange < 0)
down <- down[order(down$log2FoldChange, decreasing = TRUE), ]
outFile <- str_interp("output/${exp}_vs_${cont}_significantly_downregulated_genes.csv")
write.csv(down[, c("log2FoldChange", "padj")], file = outFile)
print(down[, c("log2FoldChange", "padj")])

Volcano Plot

as.data.frame(res) %>%
  ggplot(aes(x = log2FoldChange, y = -log10(padj), label = rownames(res))) +
  geom_point() +
  theme_minimal() +
  scale_color_manual(values = c("black", "blue", "red")) +
  geom_text_repel() +
  geom_hline(yintercept = 1.301) +
  geom_vline(xintercept = 1.2) +
  geom_vline(xintercept = -1.2) +
  xlim(-10, 10)
Warning: Removed 11747 rows containing missing values (`geom_point()`).
Warning: Removed 11747 rows containing missing values (`geom_text_repel()`).
Warning: ggrepel: 17979 unlabeled data points (too many overlaps). Consider increasing
max.overlaps

Gene Ontology

GSEA of genes differentially express in (OVCAR3B compared to control OVCAR3)

Perform gene set enrichment analysis using Cluster Profiler. This gives us GO pathways that are significantly regulated based on the log2fold change of expression of individual genes.

Using a pvalue Cutoff of 0.05

gene_list <- res$log2FoldChange
names(gene_list) <- rownames(res)
gene_list <- sort(gene_list, decreasing = TRUE)

# Set the seed so our results are reproducible:
set.seed(2023)
gsea_res <- gseGO(gene_list, ont = "BP", OrgDb = "org.Hs.eg.db", keyType = "SYMBOL", seed = TRUE, pvalueCutoff = 0.05)
preparing geneSet collections...
GSEA analysis...
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize, gseaParam, : There are ties in the preranked stats (13.29% of the list).
The order of those tied genes will be arbitrary, which may produce unexpected results.
leading edge analysis...
done...
# Format output
gsea_res_df <- as.data.frame(gsea_res)
gsea_res_df <- gsea_res_df %>%
  mutate(original_row_num = row_number())
gsea_res_df <- gsea_res_df[order(gsea_res_df$NES, decreasing = TRUE),]
row.names(gsea_res_df) <- gsea_res_df$ID

NES is the normalized enrichment score.

gsea_res_df_short <- gsea_res_df[c("pvalue", "p.adjust", "NES", "Description")]
gsea_res_df_short$"core_enrichment_genes" <- gsea_res_df$core_enrichment

Upregulated pathways

gsea_res_df_short.up <- subset(gsea_res_df_short, gsea_res_df_short$NES >= 0)
outFile <- str_interp("output/${exp}_vs_${cont}_significantly_upregulated_pathways.csv")
write.csv(gsea_res_df_short.up, file = outFile)
gsea_res_df_short.up

GSEA plot of the five most upregulated pathways (or least downregulated)

maxIndex <- min(5, nrow(gsea_res_df)) # Prevents us from trying to access out of bounds if there are not five pathways
top5PathwaysIds = gsea_res_df[1:maxIndex, "original_row_num"]

gseaplot2(gsea_res, geneSetID = top5PathwaysIds, pvalue_table = FALSE, ES_geom = "dot")

Volcano Plot (Average NES & adjusted p value)

as.data.frame(gsea_res_df_short.up) %>%
  ggplot(aes(x = NES, y = -log10(p.adjust), label = rownames(gsea_res_df_short.up))) +
  geom_point() +
  theme_minimal() +
  scale_color_manual(values = c("black", "blue", "red")) +
  geom_text_repel() +
  geom_hline(yintercept = 1.301) +
  geom_vline(xintercept = 1.2) +
  geom_vline(xintercept = -1.2) +
  xlim(-10, 10)

Downregulated pathways

gsea_res_df_short.down <- subset(gsea_res_df_short, gsea_res_df_short$NES <= 0)
outFile <- str_interp("output/${exp}_vs_${cont}_significantly_downregulated_pathways.csv")
write.csv(gsea_res_df_short.down, file = outFile)
gsea_res_df_short.down

GSEA plot of the five most downregulated pathways (or least upregulated)

minIndex <- max(1, nrow(gsea_res_df) - 5) # Prevents us from trying to access out of bounds if there are not five downregulated pathways
bottom5PathwaysIds = gsea_res_df[minIndex:nrow(gsea_res_df), "original_row_num"]
gseaplot2(gsea_res, geneSetID = bottom5PathwaysIds, pvalue_table = FALSE, ES_geom = "dot")

Volcano plot (Average NES & adjusted p value)

as.data.frame(gsea_res_df_short.down) %>%
  ggplot(aes(x = NES, y = -log10(p.adjust), label = rownames(gsea_res_df_short.down))) +
  geom_point() +
  theme_minimal() +
  scale_color_manual(values = c("black", "blue", "red")) +
  geom_text_repel() +
  geom_hline(yintercept = 1.301) +
  geom_vline(xintercept = 1.2) +
  geom_vline(xintercept = -1.2) +
  xlim(-10, 10)

Clustered pathways

Clustered upregulated pathways

Use Revigo to cluster upregulated pathways

revigo_input.cellline.up <- gsea_res_df_short.up[c("p.adjust")]
rownames(revigo_input.cellline.up) <- rownames(gsea_res_df_short.up)

simMatrix <- calculateSimMatrix(rownames(revigo_input.cellline.up),
  orgdb = "org.Hs.eg.db",
  ont = "BP",
  method = "Rel"
)
preparing gene to GO mapping data...
preparing IC data...
scores <- setNames(-log10(revigo_input.cellline.up$p.adjust), rownames(revigo_input.cellline.up))

if (nrow(revigo_input.cellline.up) > 1) {
  reducedTerms <- reduceSimMatrix(simMatrix,
    scores,
    threshold = 0.7,
    orgdb = "org.Hs.eg.db"
  )
} else {
  reducedTerms <- data.frame(matrix(ncol = 0, nrow = 0))
  print("There will be no graphs appearing below this because there were not enough significantly upregulated pathways to meaningfully cluster them")
}

Revigo interactive scatter plot. Distances represent the similarity between terms, axes are the first 2 components of a PCA plot, Each bubble indicates the representative (chosen mostly by p-value) from a cluster of terms. Size of the bubble indicates the generality of the term (large meaning a more general term).

if (nrow(reducedTerms) > 2) {
  revigo_scatterplot(simMatrix, reducedTerms)
}

Revigo heatmap plot. Similar terms clustered

if (nrow(reducedTerms) > 2) {
  heatmapPlot(simMatrix,
    reducedTerms,
    annotateParent = TRUE,
    annotationLabel = "parentTerm",
    fontsize = 6
  )
}

This is the same content, but interactive.

if (nrow(reducedTerms) > 2) {
  revigo_heatmap(simMatrix, reducedTerms)
}
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()

Revigo treemap plot. Terms grouped/colored based on parent. Space is proportional to statistical significance of the GO term (-log10(pvalue)).

if (nrow(reducedTerms) > 2) {
  treemapPlot(reducedTerms)
}

Clustered downregulated pathways

Use Revigo to cluster downregulated pathways

revigo_input.cellline.down <- gsea_res_df_short.down[c("p.adjust")]
rownames(revigo_input.cellline.down) <- rownames(gsea_res_df_short.down)

simMatrix <- calculateSimMatrix(rownames(revigo_input.cellline.down),
  orgdb = "org.Hs.eg.db",
  ont = "BP",
  method = "Rel"
)
preparing gene to GO mapping data...
preparing IC data...
scores <- setNames(-log10(revigo_input.cellline.down$p.adjust), rownames(revigo_input.cellline.down))

if (nrow(revigo_input.cellline.down) > 1) {
  reducedTerms <- reduceSimMatrix(simMatrix,
    scores,
    threshold = 0.7,
    orgdb = "org.Hs.eg.db"
  )
} else {
  reducedTerms <- data.frame(matrix(ncol = 0, nrow = 0))
  print("There will be no graphs appearing below this because there were not enough significantly downregulated pathways to meaningfully cluster them")
}
[1] "There will be no graphs appearing below this because there were not enough significantly downregulated pathways to meaningfully cluster them"

Revigo interactive scatter plot. Distances represent the similarity between terms, axes are the first 2 components of a PCA plot, Each bubble indicates the representative (chosen mostly by p-value) from a cluster of terms. Size of the bubble indicates the generality of the term (large meaning a more general term).

if (nrow(reducedTerms) > 2) {
  revigo_scatterplot(simMatrix, reducedTerms)
}

Revigo heatmap plot. Similar terms clustered

if (nrow(reducedTerms) > 2) {
  heatmapPlot(simMatrix,
    reducedTerms,
    annotateParent = TRUE,
    annotationLabel = "parentTerm",
    fontsize = 6
  )
}

This is the same content, but interactive.

if (nrow(reducedTerms) > 2) {
  revigo_heatmap(simMatrix, reducedTerms)
}

Revigo treemap plot. Terms grouped/colored based on parent. Space is proportional to statistical significance of the GO term (-log10(pvalue)).

if (nrow(reducedTerms) > 2) {
  treemapPlot(reducedTerms)
}
LS0tCnRpdGxlOiBQYXJhbWV0ZXJpemVkIHNpbmdsZSBjZWxsbGluZSB2cyBjb250cm9sCm91dHB1dDogaHRtbF9ub3RlYm9vawogIApwYXJhbXM6CiAgZXhwZXJpbWVudGFsQ2VsbExpbmU6ICJTVVBQTFkgVEhJUyIKICBjb250cm9sQ2VsbExpbmU6ICJTVVBQTFkgVEhJUyIKICBjb3VudG1hdHJpeC5hbGw6ICJTVVBQTFkgVEhJUyIKICBtZXRhZGF0YS5hbGw6ICJTVVBQTFkgVEhJUyIKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgo8IS0tIEV4cGVjdHMgdGhlIGZvbGxvd2luZyBwYXJhbWV0ZXJzOiAtLT4KPCEtLSAyLiBleHBlcmltZW50YWxDZWxsTGluZSAoZXg6ICJQRU82IikgLS0+CjwhLS0gMy4gY29udHJvbENlbGxMaW5lIChleDogIlBFTzEiKSAtLT4KPCEtLSA0LiBjb3VudG1hdHJpeC5hbGwgLS0+CjwhLS0gNS4gbWV0YWRhdGEuYWxsIC0tPgoKYGBge3IgcmVhZCBwYXJhbWV0ZXJzIGNlbGxsaW5lfQpjb250IDwtIHBhcmFtcyRjb250cm9sQ2VsbExpbmUKZXhwIDwtIHBhcmFtcyRleHBlcmltZW50YWxDZWxsTGluZQpjb3VudG1hdHJpeC5hbGwgPC0gcGFyYW1zJGNvdW50bWF0cml4LmFsbAptZXRhZGF0YS5hbGwgPC0gcGFyYW1zJG1ldGFkYXRhLmFsbApybShwYXJhbXMpICMgUmVtb3ZlIHRoZSBwYXJhbWV0ZXJzIHNvIHRoYXQgd2UgY2FuIG1ha2Ugc3Vic2VxdWVudCBwYXJhbWV0ZXJpemVkIGNhbGxzCmBgYAoKLS0tCnRpdGxlOiAiREVTZXEgQW5hbHlzaXM6IGByIGV4cGAgdnMgYHIgY29udGAiCi0tLQoKYGBge3IgbG9hZCBwYWNrYWdlcyBjZWxsbGluZSwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeSh2c24pCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoYmlvbWFSdCkKbGlicmFyeShERVNlcUFuYWx5c2lzKQpsaWJyYXJ5KFVwU2V0UikKbGlicmFyeShncHJvZmlsZXIyKQpsaWJyYXJ5KHJydmdvKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeSgib3JnLkhzLmVnLmRiIikKYGBgCgojIyBQZXJmb3JtIGFuYWx5c2lzIG9mIGByIGV4cGAgdnMgYHIgY29udGAKCiMjIEdhdGhlciBhbmQgb3JnYW5pemUgcmF3IGRhdGEKCmBgYHtyIHJlc3RyaWN0IHRvIHBhaXJ9Cm1ldGFkYXRhLnBhaXIgPC0gYXMuZGF0YS5mcmFtZShtZXRhZGF0YS5hbGwpICU+JQogIGZpbHRlcihDZWxsTGluZSA9PSBjb250IHwgQ2VsbExpbmUgPT0gZXhwKQphcy5kYXRhLmZyYW1lKG1ldGFkYXRhLnBhaXIpCgpjb3VudG1hdHJpeC5wYWlyIDwtIGNvdW50bWF0cml4LmFsbFssIG1ldGFkYXRhLnBhaXIkU2hvcnROYW1lXQphcy5kYXRhLmZyYW1lKGNvdW50bWF0cml4LnBhaXIpCmBgYAoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKClJ1biBEZXNlcSBvbiB0aGUgZGF0YSBzZXQuCgpgYGB7ciBsb2FkIGRlc2VxfQojIFNhdmluZyB0aW1lIGJ5IGp1c3QgbG9hZGluZyB0aGUgZGRzIHdlIGFscmVhZHkgcmFuIChyZWNlbnQgY2hhbmdlcyBhcmUgYWxsIGFmdGVyIHRoaXMgcG9pbnQpCmxvYWQoc3RyX2ludGVycCgiUmRhdGEvJHtleHB9X3ZzXyR7Y29udH1fZGRzLlJEYXRhIikpCmBgYAoKPCEtLSBgYGB7ciBzZXR1cCBkZXNlcSBwYWlyfSAtLT4KPCEtLSAjIEhhdmluZyB0aGUgcmVwbGljYXRlIG51bWJlciBpbiB0aGUgZGVzaWduIG1ha2VzIGl0IGEgcGFpcmVkIGRlc2lnbiAtLT4KPCEtLSBkZHMucGFpciA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KCAtLT4KPCEtLSAgIGNvdW50RGF0YSA9IGNvdW50bWF0cml4LnBhaXIsIC0tPgo8IS0tICAgY29sRGF0YSA9IG1ldGFkYXRhLnBhaXIsIC0tPgo8IS0tICAgZGVzaWduID0gfiBDZWxsTGluZSArIFJlcGxpY2F0ZSkgLS0+CjwhLS0gZGRzLnBhaXIkQ2VsbExpbmUgPC0gcmVsZXZlbChkZHMucGFpciRDZWxsTGluZSwgcmVmID0gY29udCkgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBVc2luZyBhIFdhbGQgVGVzdCBiZWNhdXNlIHdlIGFyZSBvbmx5IGNvbXBhcmluZyB0d28gY2VsbCBsaW5lcy4gLS0+CjwhLS0gYGBge3IgcGFpciBkZXNlcX0gLS0+CjwhLS0gZGRzLnBhaXIgPC0gREVTZXEoZGRzLnBhaXIsICJXYWxkIikgLS0+CjwhLS0gc2F2ZShkZHMucGFpciwgZmlsZSA9IHN0cl9pbnRlcnAoIlJkYXRhLyR7ZXhwfV92c18ke2NvbnR9X2Rkcy5SRGF0YSIpKSAtLT4KPCEtLSBgYGAgLS0+CgojIyMgUHJpbnQgREVTZXEyIHJlc3VsdHMKYGBge3IgcHJpbnQgZGVzZXEyIHJlc3VsdHN9CnJlcyA8LSByZXN1bHRzKGRkcy5wYWlyLCBjb250cmFzdCA9IGMoIkNlbGxMaW5lIiwgZXhwLCBjb250KSwgYWxwaGEgPSAwLjA1KQpyZXMgPC0gcmVzW29yZGVyKHJlcyRsb2cyRm9sZENoYW5nZSksIF0Kb3V0RmlsZSA8LSBzdHJfaW50ZXJwKCJvdXRwdXQvJHtleHB9X3ZzXyR7Y29udH1fZGVzZXFfcmVzdWx0cy5jc3YiKQp3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXMpLCBmaWxlID0gb3V0RmlsZSkKYGBgCgojIyMgRmlsdGVyIERFU2VxMiByZXN1bHRzIGZvciBzaWduaWZpY2FudCBnZW5lcwpGaWx0ZXIgcmVzIGZvciBwYWRqIDwgMC4wNQpgYGB7ciBmaWx0ZXIgcmVzfQpyZXMuZmlsdGVyZWQgPC0gYXMuZGF0YS5mcmFtZShyZXMpICU+JQogIGZpbHRlcihwYWRqIDwgMC4wNSkKICAjIGZpbHRlcihsb2cyRm9sZENoYW5nZSA+PSAxLjUgfCBsb2cyRm9sZENoYW5nZSA8PSAtMS41KQpyZXMuZmlsdGVyZWQgPC0gcmVzLmZpbHRlcmVkW29yZGVyKHJlcy5maWx0ZXJlZCRsb2cyRm9sZENoYW5nZSwgZGVjcmVhc2luZyA9IFRSVUUpLF0KcmVzLmZpbHRlcmVkCmBgYAoKIyMjIyBTaWduaWZpY2FudCAiVXAiIGdlbmVzIChgciBleHBgIGNvbXBhcmVkIHRvIGNvbnRyb2wgYHIgY29udGApCmBgYHtyIHVwIGdlbmVzfQp1cC51bmZpbHRlcmVkIDwtIHN1YnNldChyZXMsIGxvZzJGb2xkQ2hhbmdlID4gMCkKdXAudW5maWx0ZXJlZCA8LSB1cC51bmZpbHRlcmVkW29yZGVyKHVwLnVuZmlsdGVyZWQkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQpvdXRGaWxlIDwtIHN0cl9pbnRlcnAoIm91dHB1dC8ke2V4cH1fdnNfJHtjb250fV9hbGxfdXByZWd1bGF0ZWRfZ2VuZXMuY3N2IikKd3JpdGUuY3N2KHVwLnVuZmlsdGVyZWRbLCBjKCJsb2cyRm9sZENoYW5nZSIsICJwYWRqIildLCBmaWxlID0gb3V0RmlsZSkKdXAudW5maWx0ZXJlZFssIGMoImxvZzJGb2xkQ2hhbmdlIiwgInBhZGoiKV0KCnVwIDwtIHN1YnNldChyZXMuZmlsdGVyZWQsIGxvZzJGb2xkQ2hhbmdlID4gMCkKdXAgPC0gdXBbb3JkZXIodXAkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQpvdXRGaWxlIDwtIHN0cl9pbnRlcnAoIm91dHB1dC8ke2V4cH1fdnNfJHtjb250fV9zaWduaWZpY2FudGx5X3VwcmVndWxhdGVkX2dlbmVzLmNzdiIpCndyaXRlLmNzdih1cFssIGMoImxvZzJGb2xkQ2hhbmdlIiwgInBhZGoiKV0sIGZpbGUgPSBvdXRGaWxlKQpwcmludCh1cFssIGMoImxvZzJGb2xkQ2hhbmdlIiwgInBhZGoiKV0pCmBgYAoKIyMjIyBTaWduaWZpY2FudCAiRG93biIgZ2VuZXMgKGByIGV4cGAgY29tcGFyZWQgdG8gY29udHJvbCBgciBjb250YCkKYGBge3IgZG93biBnZW5lc30KZG93bi51bmZpbHRlcmVkIDwtIHN1YnNldChyZXMsIGxvZzJGb2xkQ2hhbmdlIDwgMCkKZG93bi51bmZpbHRlcmVkIDwtIGRvd24udW5maWx0ZXJlZFtvcmRlcihkb3duLnVuZmlsdGVyZWQkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmcgPSBGQUxTRSksIF0Kb3V0RmlsZSA8LSBzdHJfaW50ZXJwKCJvdXRwdXQvJHtleHB9X3ZzXyR7Y29udH1fYWxsX2Rvd25yZWd1bGF0ZWRfZ2VuZXMuY3N2IikKd3JpdGUuY3N2KGRvd24udW5maWx0ZXJlZFssIGMoImxvZzJGb2xkQ2hhbmdlIiwgInBhZGoiKV0sIGZpbGUgPSBvdXRGaWxlKQpwcmludChkb3duLnVuZmlsdGVyZWRbLCBjKCJsb2cyRm9sZENoYW5nZSIsICJwYWRqIildKQoKZG93biA8LSBzdWJzZXQocmVzLmZpbHRlcmVkLCBsb2cyRm9sZENoYW5nZSA8IDApCmRvd24gPC0gZG93bltvcmRlcihkb3duJGxvZzJGb2xkQ2hhbmdlLCBkZWNyZWFzaW5nID0gVFJVRSksIF0Kb3V0RmlsZSA8LSBzdHJfaW50ZXJwKCJvdXRwdXQvJHtleHB9X3ZzXyR7Y29udH1fc2lnbmlmaWNhbnRseV9kb3ducmVndWxhdGVkX2dlbmVzLmNzdiIpCndyaXRlLmNzdihkb3duWywgYygibG9nMkZvbGRDaGFuZ2UiLCAicGFkaiIpXSwgZmlsZSA9IG91dEZpbGUpCnByaW50KGRvd25bLCBjKCJsb2cyRm9sZENoYW5nZSIsICJwYWRqIildKQpgYGAKCiMjIyBWb2xjYW5vIFBsb3QKYGBge3IgVm9sY2Fub30KYXMuZGF0YS5mcmFtZShyZXMpICU+JQogIGdncGxvdChhZXMoeCA9IGxvZzJGb2xkQ2hhbmdlLCB5ID0gLWxvZzEwKHBhZGopLCBsYWJlbCA9IHJvd25hbWVzKHJlcykpKSArCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJibHVlIiwgInJlZCIpKSArCiAgZ2VvbV90ZXh0X3JlcGVsKCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEuMzAxKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMS4yKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gLTEuMikgKwogIHhsaW0oLTEwLCAxMCkKYGBgCgojIyBHZW5lIE9udG9sb2d5CgojIyMgR1NFQSBvZiBnZW5lcyBkaWZmZXJlbnRpYWxseSBleHByZXNzIGluIChgciBleHBgIGNvbXBhcmVkIHRvIGNvbnRyb2wgYHIgY29udGApCgpQZXJmb3JtIGdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMgdXNpbmcgQ2x1c3RlciBQcm9maWxlci4gVGhpcyBnaXZlcyB1cyBHTyBwYXRod2F5cyB0aGF0IGFyZSBzaWduaWZpY2FudGx5IHJlZ3VsYXRlZCBiYXNlZCBvbiB0aGUgbG9nMmZvbGQgY2hhbmdlIG9mIGV4cHJlc3Npb24gb2YgaW5kaXZpZHVhbCBnZW5lcy4gCgpVc2luZyBhIHB2YWx1ZSBDdXRvZmYgb2YgMC4wNQoKYGBge3J9CmdlbmVfbGlzdCA8LSByZXMkbG9nMkZvbGRDaGFuZ2UKbmFtZXMoZ2VuZV9saXN0KSA8LSByb3duYW1lcyhyZXMpCmdlbmVfbGlzdCA8LSBzb3J0KGdlbmVfbGlzdCwgZGVjcmVhc2luZyA9IFRSVUUpCgojIFNldCB0aGUgc2VlZCBzbyBvdXIgcmVzdWx0cyBhcmUgcmVwcm9kdWNpYmxlOgpzZXQuc2VlZCgyMDIzKQpnc2VhX3JlcyA8LSBnc2VHTyhnZW5lX2xpc3QsIG9udCA9ICJCUCIsIE9yZ0RiID0gIm9yZy5Icy5lZy5kYiIsIGtleVR5cGUgPSAiU1lNQk9MIiwgc2VlZCA9IFRSVUUsIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCgojIEZvcm1hdCBvdXRwdXQKZ3NlYV9yZXNfZGYgPC0gYXMuZGF0YS5mcmFtZShnc2VhX3JlcykKZ3NlYV9yZXNfZGYgPC0gZ3NlYV9yZXNfZGYgJT4lCiAgbXV0YXRlKG9yaWdpbmFsX3Jvd19udW0gPSByb3dfbnVtYmVyKCkpCmdzZWFfcmVzX2RmIDwtIGdzZWFfcmVzX2RmW29yZGVyKGdzZWFfcmVzX2RmJE5FUywgZGVjcmVhc2luZyA9IFRSVUUpLF0Kcm93Lm5hbWVzKGdzZWFfcmVzX2RmKSA8LSBnc2VhX3Jlc19kZiRJRApgYGAKCk5FUyBpcyB0aGUgbm9ybWFsaXplZCBlbnJpY2htZW50IHNjb3JlLgpgYGB7cn0KZ3NlYV9yZXNfZGZfc2hvcnQgPC0gZ3NlYV9yZXNfZGZbYygicHZhbHVlIiwgInAuYWRqdXN0IiwgIk5FUyIsICJEZXNjcmlwdGlvbiIpXQpnc2VhX3Jlc19kZl9zaG9ydCQiY29yZV9lbnJpY2htZW50X2dlbmVzIiA8LSBnc2VhX3Jlc19kZiRjb3JlX2VucmljaG1lbnQKYGBgCgojIyMjIFVwcmVndWxhdGVkIHBhdGh3YXlzCgpgYGB7cn0KZ3NlYV9yZXNfZGZfc2hvcnQudXAgPC0gc3Vic2V0KGdzZWFfcmVzX2RmX3Nob3J0LCBnc2VhX3Jlc19kZl9zaG9ydCRORVMgPj0gMCkKb3V0RmlsZSA8LSBzdHJfaW50ZXJwKCJvdXRwdXQvJHtleHB9X3ZzXyR7Y29udH1fc2lnbmlmaWNhbnRseV91cHJlZ3VsYXRlZF9wYXRod2F5cy5jc3YiKQp3cml0ZS5jc3YoZ3NlYV9yZXNfZGZfc2hvcnQudXAsIGZpbGUgPSBvdXRGaWxlKQpnc2VhX3Jlc19kZl9zaG9ydC51cApgYGAKCkdTRUEgcGxvdCBvZiB0aGUgZml2ZSBtb3N0IHVwcmVndWxhdGVkIHBhdGh3YXlzIChvciBsZWFzdCBkb3ducmVndWxhdGVkKQoKYGBge3J9Cm1heEluZGV4IDwtIG1pbig1LCBucm93KGdzZWFfcmVzX2RmKSkgIyBQcmV2ZW50cyB1cyBmcm9tIHRyeWluZyB0byBhY2Nlc3Mgb3V0IG9mIGJvdW5kcyBpZiB0aGVyZSBhcmUgbm90IGZpdmUgcGF0aHdheXMKdG9wNVBhdGh3YXlzSWRzID0gZ3NlYV9yZXNfZGZbMTptYXhJbmRleCwgIm9yaWdpbmFsX3Jvd19udW0iXQoKZ3NlYXBsb3QyKGdzZWFfcmVzLCBnZW5lU2V0SUQgPSB0b3A1UGF0aHdheXNJZHMsIHB2YWx1ZV90YWJsZSA9IEZBTFNFLCBFU19nZW9tID0gImRvdCIpCmBgYAoKVm9sY2FubyBQbG90IChBdmVyYWdlIE5FUyAmIGFkanVzdGVkIHAgdmFsdWUpCgpgYGB7ciBWb2xjYW5vIGNlbGxsaW5lIHVwfQphcy5kYXRhLmZyYW1lKGdzZWFfcmVzX2RmX3Nob3J0LnVwKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBORVMsIHkgPSAtbG9nMTAocC5hZGp1c3QpLCBsYWJlbCA9IHJvd25hbWVzKGdzZWFfcmVzX2RmX3Nob3J0LnVwKSkpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgImJsdWUiLCAicmVkIikpICsKICBnZW9tX3RleHRfcmVwZWwoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS4zMDEpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxLjIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAtMS4yKSArCiAgeGxpbSgtMTAsIDEwKQpgYGAKCiMjIyMgRG93bnJlZ3VsYXRlZCBwYXRod2F5cwoKYGBge3J9CmdzZWFfcmVzX2RmX3Nob3J0LmRvd24gPC0gc3Vic2V0KGdzZWFfcmVzX2RmX3Nob3J0LCBnc2VhX3Jlc19kZl9zaG9ydCRORVMgPD0gMCkKb3V0RmlsZSA8LSBzdHJfaW50ZXJwKCJvdXRwdXQvJHtleHB9X3ZzXyR7Y29udH1fc2lnbmlmaWNhbnRseV9kb3ducmVndWxhdGVkX3BhdGh3YXlzLmNzdiIpCndyaXRlLmNzdihnc2VhX3Jlc19kZl9zaG9ydC5kb3duLCBmaWxlID0gb3V0RmlsZSkKZ3NlYV9yZXNfZGZfc2hvcnQuZG93bgpgYGAKCkdTRUEgcGxvdCBvZiB0aGUgZml2ZSBtb3N0IGRvd25yZWd1bGF0ZWQgcGF0aHdheXMgKG9yIGxlYXN0IHVwcmVndWxhdGVkKQoKYGBge3J9Cm1pbkluZGV4IDwtIG1heCgxLCBucm93KGdzZWFfcmVzX2RmKSAtIDUpICMgUHJldmVudHMgdXMgZnJvbSB0cnlpbmcgdG8gYWNjZXNzIG91dCBvZiBib3VuZHMgaWYgdGhlcmUgYXJlIG5vdCBmaXZlIGRvd25yZWd1bGF0ZWQgcGF0aHdheXMKYm90dG9tNVBhdGh3YXlzSWRzID0gZ3NlYV9yZXNfZGZbbWluSW5kZXg6bnJvdyhnc2VhX3Jlc19kZiksICJvcmlnaW5hbF9yb3dfbnVtIl0KZ3NlYXBsb3QyKGdzZWFfcmVzLCBnZW5lU2V0SUQgPSBib3R0b201UGF0aHdheXNJZHMsIHB2YWx1ZV90YWJsZSA9IEZBTFNFLCBFU19nZW9tID0gImRvdCIpCmBgYAoKVm9sY2FubyBwbG90IChBdmVyYWdlIE5FUyAmIGFkanVzdGVkIHAgdmFsdWUpCgpgYGB7ciBWb2xjYW5vIGNlbGxsaW5lIGRvd259CmFzLmRhdGEuZnJhbWUoZ3NlYV9yZXNfZGZfc2hvcnQuZG93bikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gTkVTLCB5ID0gLWxvZzEwKHAuYWRqdXN0KSwgbGFiZWwgPSByb3duYW1lcyhnc2VhX3Jlc19kZl9zaG9ydC5kb3duKSkpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgImJsdWUiLCAicmVkIikpICsKICBnZW9tX3RleHRfcmVwZWwoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS4zMDEpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxLjIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAtMS4yKSArCiAgeGxpbSgtMTAsIDEwKQpgYGAKCiMjIyBDbHVzdGVyZWQgcGF0aHdheXMKCiMjIyMgQ2x1c3RlcmVkIHVwcmVndWxhdGVkIHBhdGh3YXlzCgpVc2UgUmV2aWdvIHRvIGNsdXN0ZXIgdXByZWd1bGF0ZWQgcGF0aHdheXMKCmBgYHtyIHVwcmVnIGNsdXN0ZXIgY2VsbGxpbmV9CnJldmlnb19pbnB1dC5jZWxsbGluZS51cCA8LSBnc2VhX3Jlc19kZl9zaG9ydC51cFtjKCJwLmFkanVzdCIpXQpyb3duYW1lcyhyZXZpZ29faW5wdXQuY2VsbGxpbmUudXApIDwtIHJvd25hbWVzKGdzZWFfcmVzX2RmX3Nob3J0LnVwKQoKc2ltTWF0cml4IDwtIGNhbGN1bGF0ZVNpbU1hdHJpeChyb3duYW1lcyhyZXZpZ29faW5wdXQuY2VsbGxpbmUudXApLAogIG9yZ2RiID0gIm9yZy5Icy5lZy5kYiIsCiAgb250ID0gIkJQIiwKICBtZXRob2QgPSAiUmVsIgopCnNjb3JlcyA8LSBzZXROYW1lcygtbG9nMTAocmV2aWdvX2lucHV0LmNlbGxsaW5lLnVwJHAuYWRqdXN0KSwgcm93bmFtZXMocmV2aWdvX2lucHV0LmNlbGxsaW5lLnVwKSkKCmlmIChucm93KHJldmlnb19pbnB1dC5jZWxsbGluZS51cCkgPiAxKSB7CiAgcmVkdWNlZFRlcm1zIDwtIHJlZHVjZVNpbU1hdHJpeChzaW1NYXRyaXgsCiAgICBzY29yZXMsCiAgICB0aHJlc2hvbGQgPSAwLjcsCiAgICBvcmdkYiA9ICJvcmcuSHMuZWcuZGIiCiAgKQp9IGVsc2UgewogIHJlZHVjZWRUZXJtcyA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gMCwgbnJvdyA9IDApKQogIHByaW50KCJUaGVyZSB3aWxsIGJlIG5vIGdyYXBocyBhcHBlYXJpbmcgYmVsb3cgdGhpcyBiZWNhdXNlIHRoZXJlIHdlcmUgbm90IGVub3VnaCBzaWduaWZpY2FudGx5IHVwcmVndWxhdGVkIHBhdGh3YXlzIHRvIG1lYW5pbmdmdWxseSBjbHVzdGVyIHRoZW0iKQp9CgpgYGAKClJldmlnbyBpbnRlcmFjdGl2ZSBzY2F0dGVyIHBsb3QuIERpc3RhbmNlcyByZXByZXNlbnQgdGhlIHNpbWlsYXJpdHkgYmV0d2VlbiB0ZXJtcywgYXhlcyBhcmUgdGhlIGZpcnN0IDIgY29tcG9uZW50cyBvZiBhIFBDQSBwbG90LCBFYWNoIGJ1YmJsZSBpbmRpY2F0ZXMgdGhlIHJlcHJlc2VudGF0aXZlIChjaG9zZW4gbW9zdGx5IGJ5IHAtdmFsdWUpIGZyb20gYSBjbHVzdGVyIG9mIHRlcm1zLiBTaXplIG9mIHRoZSBidWJibGUgaW5kaWNhdGVzIHRoZSBnZW5lcmFsaXR5IG9mIHRoZSB0ZXJtIChsYXJnZSBtZWFuaW5nIGEgbW9yZSBnZW5lcmFsIHRlcm0pLgoKYGBge3IgdXByZWcgY2x1c3RlciBjZWxsbGluZSBzY2F0dGVycGxvdH0KaWYgKG5yb3cocmVkdWNlZFRlcm1zKSA+IDIpIHsKICByZXZpZ29fc2NhdHRlcnBsb3Qoc2ltTWF0cml4LCByZWR1Y2VkVGVybXMpCn0KCmBgYAoKUmV2aWdvIGhlYXRtYXAgcGxvdC4gU2ltaWxhciB0ZXJtcyBjbHVzdGVyZWQKCmBgYHtyIHVwcmVnIGNsdXN0ZXIgY2VsbGxpbmUgaGVhdG1hcH0KaWYgKG5yb3cocmVkdWNlZFRlcm1zKSA+IDIpIHsKICBoZWF0bWFwUGxvdChzaW1NYXRyaXgsCiAgICByZWR1Y2VkVGVybXMsCiAgICBhbm5vdGF0ZVBhcmVudCA9IFRSVUUsCiAgICBhbm5vdGF0aW9uTGFiZWwgPSAicGFyZW50VGVybSIsCiAgICBmb250c2l6ZSA9IDYKICApCn0KYGBgCgpUaGlzIGlzIHRoZSBzYW1lIGNvbnRlbnQsIGJ1dCBpbnRlcmFjdGl2ZS4KCmBgYHtyIHVwcmVnIGNsdXN0ZXIgY2VsbGxpbmUgaGVhdG1hcDJ9CmlmIChucm93KHJlZHVjZWRUZXJtcykgPiAyKSB7CiAgcmV2aWdvX2hlYXRtYXAoc2ltTWF0cml4LCByZWR1Y2VkVGVybXMpCn0KYGBgCgpSZXZpZ28gdHJlZW1hcCBwbG90LiBUZXJtcyBncm91cGVkL2NvbG9yZWQgYmFzZWQgb24gcGFyZW50LiBTcGFjZSBpcyBwcm9wb3J0aW9uYWwgdG8gc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIG9mIHRoZSBHTyB0ZXJtICgtbG9nMTAocHZhbHVlKSkuCgpgYGB7ciB1cHJlZyBjbHVzdGVyIGNlbGxsaW5lIHRyZWVtYXB9CmlmIChucm93KHJlZHVjZWRUZXJtcykgPiAyKSB7CiAgdHJlZW1hcFBsb3QocmVkdWNlZFRlcm1zKQp9CmBgYAoKIyMjIyBDbHVzdGVyZWQgZG93bnJlZ3VsYXRlZCBwYXRod2F5cwoKVXNlIFJldmlnbyB0byBjbHVzdGVyIGRvd25yZWd1bGF0ZWQgcGF0aHdheXMKCgpgYGB7ciBkb3ducmVnIGNsdXN0ZXIgY2VsbGxpbmV9CnJldmlnb19pbnB1dC5jZWxsbGluZS5kb3duIDwtIGdzZWFfcmVzX2RmX3Nob3J0LmRvd25bYygicC5hZGp1c3QiKV0Kcm93bmFtZXMocmV2aWdvX2lucHV0LmNlbGxsaW5lLmRvd24pIDwtIHJvd25hbWVzKGdzZWFfcmVzX2RmX3Nob3J0LmRvd24pCgpzaW1NYXRyaXggPC0gY2FsY3VsYXRlU2ltTWF0cml4KHJvd25hbWVzKHJldmlnb19pbnB1dC5jZWxsbGluZS5kb3duKSwKICBvcmdkYiA9ICJvcmcuSHMuZWcuZGIiLAogIG9udCA9ICJCUCIsCiAgbWV0aG9kID0gIlJlbCIKKQoKc2NvcmVzIDwtIHNldE5hbWVzKC1sb2cxMChyZXZpZ29faW5wdXQuY2VsbGxpbmUuZG93biRwLmFkanVzdCksIHJvd25hbWVzKHJldmlnb19pbnB1dC5jZWxsbGluZS5kb3duKSkKCmlmIChucm93KHJldmlnb19pbnB1dC5jZWxsbGluZS5kb3duKSA+IDEpIHsKICByZWR1Y2VkVGVybXMgPC0gcmVkdWNlU2ltTWF0cml4KHNpbU1hdHJpeCwKICAgIHNjb3JlcywKICAgIHRocmVzaG9sZCA9IDAuNywKICAgIG9yZ2RiID0gIm9yZy5Icy5lZy5kYiIKICApCn0gZWxzZSB7CiAgcmVkdWNlZFRlcm1zIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSAwLCBucm93ID0gMCkpCiAgcHJpbnQoIlRoZXJlIHdpbGwgYmUgbm8gZ3JhcGhzIGFwcGVhcmluZyBiZWxvdyB0aGlzIGJlY2F1c2UgdGhlcmUgd2VyZSBub3QgZW5vdWdoIHNpZ25pZmljYW50bHkgZG93bnJlZ3VsYXRlZCBwYXRod2F5cyB0byBtZWFuaW5nZnVsbHkgY2x1c3RlciB0aGVtIikKfQpgYGAKClJldmlnbyBpbnRlcmFjdGl2ZSBzY2F0dGVyIHBsb3QuIERpc3RhbmNlcyByZXByZXNlbnQgdGhlIHNpbWlsYXJpdHkgYmV0d2VlbiB0ZXJtcywgYXhlcyBhcmUgdGhlIGZpcnN0IDIgY29tcG9uZW50cyBvZiBhIFBDQSBwbG90LCBFYWNoIGJ1YmJsZSBpbmRpY2F0ZXMgdGhlIHJlcHJlc2VudGF0aXZlIChjaG9zZW4gbW9zdGx5IGJ5IHAtdmFsdWUpIGZyb20gYSBjbHVzdGVyIG9mIHRlcm1zLiBTaXplIG9mIHRoZSBidWJibGUgaW5kaWNhdGVzIHRoZSBnZW5lcmFsaXR5IG9mIHRoZSB0ZXJtIChsYXJnZSBtZWFuaW5nIGEgbW9yZSBnZW5lcmFsIHRlcm0pLgoKYGBge3IgZG93bnJlZyBjbHVzdGVyIGNlbGxsaW5lIHNjYXR0ZXJwbG90fQppZiAobnJvdyhyZWR1Y2VkVGVybXMpID4gMikgewogIHJldmlnb19zY2F0dGVycGxvdChzaW1NYXRyaXgsIHJlZHVjZWRUZXJtcykKfQpgYGAKClJldmlnbyBoZWF0bWFwIHBsb3QuIFNpbWlsYXIgdGVybXMgY2x1c3RlcmVkCgpgYGB7ciBkb3ducmVnIGNsdXN0ZXIgY2VsbGxpbmUgaGVhdG1hcH0KaWYgKG5yb3cocmVkdWNlZFRlcm1zKSA+IDIpIHsKICBoZWF0bWFwUGxvdChzaW1NYXRyaXgsCiAgICByZWR1Y2VkVGVybXMsCiAgICBhbm5vdGF0ZVBhcmVudCA9IFRSVUUsCiAgICBhbm5vdGF0aW9uTGFiZWwgPSAicGFyZW50VGVybSIsCiAgICBmb250c2l6ZSA9IDYKICApCn0KYGBgCgpUaGlzIGlzIHRoZSBzYW1lIGNvbnRlbnQsIGJ1dCBpbnRlcmFjdGl2ZS4KCmBgYHtyIGRvd25yZWcgY2x1c3RlciBjZWxsbGluZSBoZWF0bWFwMn0KaWYgKG5yb3cocmVkdWNlZFRlcm1zKSA+IDIpIHsKICByZXZpZ29faGVhdG1hcChzaW1NYXRyaXgsIHJlZHVjZWRUZXJtcykKfQpgYGAKClJldmlnbyB0cmVlbWFwIHBsb3QuIFRlcm1zIGdyb3VwZWQvY29sb3JlZCBiYXNlZCBvbiBwYXJlbnQuIFNwYWNlIGlzIHByb3BvcnRpb25hbCB0byBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgdGhlIEdPIHRlcm0gKC1sb2cxMChwdmFsdWUpKS4KCmBgYHtyIGRvd25yZWcgY2x1c3RlciBjZWxsbGluZSB0cmVlbWFwfQppZiAobnJvdyhyZWR1Y2VkVGVybXMpID4gMikgewogIHRyZWVtYXBQbG90KHJlZHVjZWRUZXJtcykKfQpgYGAK